/**
*********************************************************************************************************
*               Copyright(c) 2018, Realtek Semiconductor Corporation. All rights reserved.
**********************************************************************************************************
* @file     dataTrans_hci.c
* @brief    This file provides hci protocol driver.
* @details
* @author   elliot_chen
* @date     2018-11-16
* @version  v1.0
*********************************************************************************************************
*/

/* Includes ------------------------------------------------------------------*/
///#define LOG_TAG    "TRANSHCI"
#include "asr_rtl8763bo_port.h"

#if USE_LV_BLUETOOTH != 0

#include "dataTrans_hci.h"

#include "hci_cmd.h"
#include "hci_app.h"

/* Gloabls -------------------------------------------------------------------*/
/* Store HCI transmission packet and control infomation */
HCI_TransInfoTypeDef hci_trans_info;

extern LoopQueue_TypeDef   LoopQueue;

#ifdef DEBUG_UART_TX_HCI_PACKET
static const char * parseCmdName(uint16_t opcode)
{
    const char * stateStr = NULL;

    switch(opcode) {
        case HCI_CMD_ACK:
            stateStr = "C_ACK";
            break;
        case HCI_CMD_GET_PAIDED_RECORD:
            stateStr = "C_GET_PAIDED_RECORD";
            break;
        case HCI_CMD_CREATE_CONNECTION:
            stateStr = "C_CREATE_CONNECTION";
            break;
        case HCI_CMD_DISCONNECT:
            stateStr = "C_DISCONNECT";
            break;
        case HCI_CMD_MMI:
            stateStr = "C_MMI";
            break;
        case HCI_CMD_GET_STATUS:
            stateStr = "C_GET_STATUS";
            break;
        case HCI_CMD_WAKE_UP:
            stateStr = "C_WAKE_UP";
            break;
        case HCI_CMD_HFP_DIAL_WITH_NUMBER:
            stateStr = "C_HFP_DIAL_WITH_NUMBER";
            break;
        case HCI_CMD_GET_BT_ADDR:
            stateStr = "C_GET_BT_ADDR";
            break;
        case HCI_CMD_PUSH_TONE:
            stateStr = "C_PUSH_TONE";
            break;
        case HCI_CMD_INQUIRY:
            stateStr = "C_INQUIRY";
            break;
        case HCI_CMD_STOP:
            stateStr = "C_STOP";
            break;
        case HCI_CMD_REMOTE_NAME_REQ:
            stateStr = "C_REMOTE_NAME_REQ";
            break;
        case HCI_CMD_GET_PLAY_LIST:
            stateStr = "C_GET_PLAY_LIST";
            break;
        case HCI_CMD_PLAY_MEDIA_BY_NAME:
            stateStr = "C_PLAY_MEDIA_BY_NAME";
            break;
        case HCI_CMD_PLAY_MEDIA_BY_INDEX:
            stateStr = "C_PLAY_MEDIA_BY_INDEX";
            break;
        case HCI_CMD_GET_MEDIA_INFO:
            stateStr = "C_GET_MEDIA_INFO";
            break;
        case HCI_CMD_GET_PLAY_STATUS:
            stateStr = "C_GET_PLAY_STATUS";
            break;
        case HCI_CMD_SET_PLAY_MODE:
            stateStr = "C_SET_PLAY_MODE";
            break;
        case HCI_CMD_SET_SYSTEM_RESET:
            stateStr = "C_SET_SYSTEM_RESET";
            break;
        case HCI_CMD_REQ_SBC_OPERATION:
            stateStr = "C_REQ_SBC_OPERATION";
            break;
        case HCI_CMD_SBC_FRAME:
            stateStr = "C_SBC_FRAME";
            break;
        case HCI_CMD_I2S_STATUS:
            stateStr = "C_I2S_STATUS";
            break;
        case HCI_CMD_AG_INDICATOR:
            stateStr = "C_AG_INDICATOR";
            break;
        case HCI_CMD_REQ_AG_OPERATION:
            stateStr = "C_REQ_AG_OPERATION";
            break;
        case HCI_CMD_REQ_HF_INDICATOR:
            stateStr = "C_REQ_HF_INDICATOR";
            break;
        case HCI_CMD_SET_HF_VOLUME:
            stateStr = "C_SET_HF_VOLUME";
            break;
        case HCI_CMD_INQUIRY_START:
            stateStr = "C_INQUIRY_START";
            break;
        case HCI_CMD_INQUIRY_STOP:
            stateStr = "C_INQUIRY_STOP";
            break;

        default:
            stateStr = "C_UNKNOWN";
            break;
    }

    return stateStr;
}
#endif
static const char * parseEventName(uint16_t opcode)
{
    const char * stateStr = NULL;

    switch(opcode) {
        case HCI_EVENT_ACK:
            stateStr = "E_ACK";
            break;
        case HCI_EVENT_REPLY_PAIRED_RECORD:
            stateStr = "E_REPLY_PAIRED_RECORD";
            break;
        case HCI_EVENT_CREATE_CONNECTION:
            stateStr = "E_CREATE_CONNECTION";
            break;
        case HCI_EVENT_DISCONNECT:
            stateStr = "E_DISCONNECT";
            break;
        case HCI_EVENT_DEVICE_STATE:
            stateStr = "E_DEVICE_STATE";
            break;
        case HCI_EVENT_CALL_STATUS:
            stateStr = "E_CALL_STATUS";
            break;
        case HCI_EVENT_PLAYER_STATUS:
            stateStr = "E_PLAYER_STATUS";
            break;
        case HCI_EVENT_LINK_STATUS:
            stateStr = "E_LINK_STATUS";
            break;
        case HCI_EVENT_CODEC_SETTING:
            stateStr = "E_CODEC_SETTING";
            break;
        case HCI_EVENT_INITIATE_CONN_DONE:
            stateStr = "E_INITIATE_CONN_DONE";
            break;
        case HCI_EVENT_REPORT_STATUS:
            stateStr = "E_REPORT_STATUS";
            break;
        case HCI_EVENT_PAIRING_KEY_MISSING:
            stateStr = "E_PAIRING_KEY_MISSING";
            break;
        case HCI_EVENT_LE_CONNECTED:
            stateStr = "E_LE_CONNECTED";
            break;
        case HCI_EVENT_LE_DISCONNECTED:
            stateStr = "E_LE_DISCONNECTED";
            break;
        case HCI_EVENT_LE_PAIR_STATUS:
            stateStr = "E_LE_PAIR_STATUS";
            break;
        case HCI_EVENT_LE_DATA_TRANSFER:
            stateStr = "E_LE_DATA_TRANSFER";
            break;
        case HCI_EVENT_DSP_IDLE:
            stateStr = "E_DSP_IDLE";
            break;
        case HCI_EVENT_PUBLIC_ADDR:
            stateStr = "E_PUBLIC_ADDR";
            break;
        case HCI_EVENT_GET_MMI:
            stateStr = "E_GET_MMI";
            break;
        case HCI_EVENT_SCO_STATUS:
            stateStr = "E_SCO_STATUS";
            break;
        case HCI_EVENT_MEDIA_INFO:
            stateStr = "E_MEDIA_INFO";
            break;
        case HCI_EVENT_MEDIA_INFO_SYNC_START:
            stateStr = "E_MEDIA_INFO_SYNC_START";
            break;
        case HCI_EVENT_MEDIA_INFO_SYNC_END:
            stateStr = "E_MEDIA_INFO_SYNC_END";
            break;
        case HCI_EVENT_PLAY_STATUS:
            stateStr = "E_PLAY_STATUS";
            break;
        case HCI_EVENT_MODE_STATE:
            stateStr = "E_MODE_STATE";
            break;
        case HCI_EVENT_PLAYLIST_CHANGED:
            stateStr = "E_PLAYLIST_CHANGED";
            break;
        case HCI_EVENT_REQ_MEDIA_FRAME:
            stateStr = "E_REQ_MEDIA_FRAME";
            break;
        case HCI_EVENT_MIC_MUTE_STATUS:
            stateStr = "E_MIC_MUTE_STATUS";
            break;
        case HCI_EVENT_AUTHEN_FAIL:
            stateStr = "E_AUTHEN_FAIL";
            break;
        case HCI_EVENT_VOLUME_STATUS:
            stateStr = "E_VOLUME_STATUS";
            break;
        case HCI_EVENT_MEDIA_INFO_SYNC_ERR:
            stateStr = "E_MEDIA_INFO_SYNC_ERR";
            break;
        case HCI_EVENT_SBC_FORMAT:
            stateStr = "E_SBC_FORMAT";
            break;
        case HCI_EVENT_REQ_SBC_FRAME:
            stateStr = "E_REQ_SBC_FRAME";
            break;
        case HCI_EVENT_WATERWORLD_PLAY_STATUS:
            stateStr = "E_WATERWORLD_PLAY_STATUS";
            break;
        case HCI_EVENT_I2S_REQUIREMENT:
            stateStr = "E_I2S_REQUIREMENT";
            break;
        case HCI_EVENT_REQ_AG_INDICATOR:
            stateStr = "E_REQ_AG_INDICATOR";
            break;
        case HCI_EVENT_REQ_AG_OPERATION:
            stateStr = "E_REQ_AG_OPERATION";
            break;
        case HCI_EVENT_HF_INDICATOR:
            stateStr = "E_HF_INDICATOR";
            break;
        case HCI_EVENT_HF_VOLUME_SYNC:
            stateStr = "E_HF_VOLUME_SYNC";
            break;
        case HCI_EVENT_BLE_ADV_STATUS:
            stateStr = "E_BLE_ADV_STATUS";
            break;
        case HCI_EVENT_AUDIO_DATA:
            stateStr = "E_AUDIO_DATA";
            break;
        case HCI_EVENT_AUDIO_CONTROL:
            stateStr = "E_AUDIO_CONTROL";
            break;
        case HCI_EVENT_INQURIRY_START:
            stateStr = "E_INQURIRY_START";
            break;
        case HCI_EVENT_INQURIRY_COMPLETE:
            stateStr = "E_INQURIRY_COMPLETE";
            break;
        case HCI_EVENT_INQURIRY_STOP:
            stateStr = "E_INQURIRY_STOP";
            break;
        case HCI_EVENT_INQURIRY_RESULT:
            stateStr = "E_INQURIRY_RESULT";
            break;
        case HCI_EVENT_REPORT_ERR_CODE:
            stateStr = "E_REPORT_ERR_CODE";
            break;

        default:
            stateStr = "E_UNKNOWN";
            break;
    }

    return stateStr;
}
char * snprintf_buffer(uint8_t * payLoadBuf, uint16_t len)
{
#define PARAM_QUOTE 10  /// "param: []"  9+1bytes
    char * buffer;
    int i, n, param_len, buffer_len;

    param_len = len;
    buffer_len = 256;///param_len*3+PARAM_QUOTE + 4;  /// 3: 1 param + 1 comma + 1 space. 4: 4bytes left to check out of array bound
    buffer = (char *)malloc(buffer_len);
    if(buffer) {
        memset(buffer, 0, buffer_len);
        n = 0;
        n += snprintf(&buffer[n], buffer_len - n, "%s", "param: [");
        for(i = 0; i < param_len; i++) {
            if(n >= buffer_len - 3) { // MAX-1 = '\0' MAX-2 = ']'
                break;
            }

            n += snprintf(&buffer[n], buffer_len - n, "%d", payLoadBuf[i]);

            if(i < param_len - 1) {
                n += snprintf(&buffer[n], buffer_len - n, "%s", ", ");
            }
        }
        n += snprintf(&buffer[n], buffer_len - n, "%s", "]");

        if(buffer[buffer_len - 1] + buffer[buffer_len - 2] + buffer[buffer_len - 3] + buffer[buffer_len - 4]) {
            MODLOGE("snprintf out of bound!!\n");
            MODLOGE("param_len: %d, buffer_len: %d, n: %d", param_len, buffer_len, n);
        }

        /// will be free(buffer) at caller
        ///free(buffer);
    }

    return buffer;
}

/**
  * @brief  initialize hci data struct which store hci information.
  * @param  none.
  * @retval none.
  */
void DataTrans_hci_data_struct_init(void)
{
    memset(&hci_trans_info, 0, sizeof(HCI_TransInfoTypeDef));
}

/**
  * @brief  check packet.
  * @param  dataPtr: buffer addr.
  * @param  len: buffer length.
  * @retval Check status of a entire packet.
  */
uint8_t DataTrans_calc_checksum(uint8_t * dataPtr, uint16_t len)
{
    uint8_t check_sum;

    check_sum = 0;
    while(len) {
        check_sum += *dataPtr;
        dataPtr++;
        len--;
    }
    return (0xff - check_sum + 1); //((~check_sum)+1);
}

/**
  * @brief  Check seqn field in HCI packet.
  * @param  pPacket: point to HCI packet struct.
  * @retval Check status of seqn field.
  */
static bool DataTrans_CheckSeqn(HCI_PackTypeDef * pPacket)
{
    bool check_seqn_flag = true;

    if(hci_trans_info.hci_rx_seqn == pPacket->seqn) {
        check_seqn_flag = false;
    }

    hci_trans_info.hci_rx_seqn = pPacket->seqn;
    return check_seqn_flag;
}

static bool DataTrans_CheckAudioSeqn(HCI_PackTypeDef * pPacket)
{
    bool check_seqn_flag = true;
    uint16_t audio_seqn_num = 0;

    /* check audio seqn num*/
    audio_seqn_num = (pPacket->payload[1] << 8) | pPacket->payload[0];

    if(audio_seqn_num > hci_trans_info.pcm_rx_seqn) {
        if(audio_seqn_num - hci_trans_info.pcm_rx_seqn > 1) {
            MODLOGD("lost packets %d,%d", audio_seqn_num, hci_trans_info.pcm_rx_seqn);
            check_seqn_flag = false;
        }
    }
    if(audio_seqn_num < hci_trans_info.pcm_rx_seqn) {
        if(audio_seqn_num >= 1) {
            MODLOGD("lost packets %d,%d", audio_seqn_num, hci_trans_info.pcm_rx_seqn);
            check_seqn_flag = false;
        }
    }

    hci_trans_info.pcm_rx_seqn = audio_seqn_num;

    return check_seqn_flag;
}

static bool DataTrans_CheckAudioLen(HCI_PackTypeDef * pPacket)
{
    bool check_len_flag = true;

    if(pPacket->len == (HCI_OPCODE_WIDTH + HCI_AUDIO_DATA_SEQN_WIDTH + HCI_AUDIO_DATA_WIDTH)) {
        ///MODLOGD("complete");
    } else {
        ///MODLOGD("broken");
        check_len_flag = false;
    }
    return check_len_flag;
}


/**
  * @brief  Fill in data to HCI packet.
  * @param  pPacket: point to HCI packet struct.
  * @retval Check status of a entire packet.
  */
static bool DataTrans_CollectHCIPacket(HCI_PackTypeDef * pPacket, uint16_t len)
{
    uint8_t buf[HCI_HEADER_AND_OPCODE_LEN], local_check;
    uint16_t pre_opcode, opcode;

    memset(buf, 0, sizeof(buf));
    /* check entire packet or not */
    DataTrans_CopyBuf(buf, HCI_HEADER_AND_OPCODE_LEN);

    opcode = ((buf[5] << 8) | buf[4]);
    if(opcode == HCI_EVENT_AUDIO_DATA) {
        DataTrans_ReadQueue(&(pPacket->sync_word), HCI_SYNC_WORD_WIDTH);
        DataTrans_ReadQueue(&(pPacket->seqn), HCI_SEQN_WIDTH);
        DataTrans_ReadQueue((uint8_t *) & (pPacket->len), HCI_PACKET_LEN_WIDTH);
        DataTrans_ReadQueue((uint8_t *) & (pPacket->opcode), HCI_OPCODE_WIDTH);
        /* HCI_CMD_AUDIO_DATA omit to len/checkerr field */

        if(pPacket->len >= HCI_OPCODE_WIDTH) {
            DataTrans_ReadQueue(pPacket->payload, pPacket->len - HCI_OPCODE_WIDTH);

            /* check audio seqn num*/
            DataTrans_CheckAudioSeqn(pPacket);

            DataTrans_ReadQueue(&(pPacket->check_sum), HCI_CHECK_SUM_WIDTH);

            if(!DataTrans_CheckAudioLen(pPacket)) {
                MODLOGD("[broken]");
                //return false;
            }

            /* Check sum field */
            if(pPacket->check_sum != (local_check = DataTrans_calc_checksum(&(pPacket->seqn),
                                                    pPacket->len + HCI_SEQN_WIDTH + HCI_PACKET_LEN_WIDTH))) {
                MODLOGD("[checkerr]");
                //return false;
            }

            return true;
        } else {
            goto out;
        }
    }

    /* Little endian */
    if((pPacket->len = (buf[HCI_PACKET_LEN_INDEX + 1] << 8) + buf[HCI_PACKET_LEN_INDEX]) <=
            (len - HCI_HEADER_AND_CHECK_SUM_LEN)) {
        DataTrans_ReadQueue(&(pPacket->sync_word), HCI_SYNC_WORD_WIDTH);
        DataTrans_ReadQueue(&(pPacket->seqn), HCI_SEQN_WIDTH);
        DataTrans_ReadQueue((uint8_t *) & (pPacket->len), HCI_PACKET_LEN_WIDTH);
        DataTrans_ReadQueue((uint8_t *) & (pPacket->opcode), HCI_OPCODE_WIDTH);

        if(pPacket->len >= HCI_OPCODE_WIDTH) {
            DataTrans_ReadQueue(pPacket->payload, pPacket->len - HCI_OPCODE_WIDTH);
        } else {
            MODLOGD("[lenerr]rx:%02X,op:%04x,len:%d,%s",
                    pPacket->seqn, pPacket->opcode, pPacket->len, parseEventName(pPacket->opcode));
#if 0
            MODLOGD("LoopQueue======================");
            for(i = 0; i < LOOP_QUEUE_MAX_SIZE; i++) {
                if(i % 16 == 0)
                    fatal_printf("\n");

                fatal_printf("%02X", LoopQueue.buf[i]);
            }
            MODLOGD("================================");
            fatal_printf("\n");

#endif
            return false;
        }
        DataTrans_ReadQueue(&(pPacket->check_sum), HCI_CHECK_SUM_WIDTH);

        /* Check sum field */
        if(pPacket->check_sum != (local_check = DataTrans_calc_checksum(&(pPacket->seqn),
                                                pPacket->len + HCI_SEQN_WIDTH + HCI_PACKET_LEN_WIDTH))) {
            pre_opcode = ((pPacket->payload[1] << 8) | pPacket->payload[0]);
            if((pPacket->opcode == HCI_EVENT_ACK) || (pPacket->opcode == HCI_EVENT_REPORT_ERR_CODE))
                MODLOGD("[checkerr]rx:%02X,op:%04x,sum:%02X(%02X),%s(%04X)",
                        pPacket->seqn, pPacket->opcode, pPacket->check_sum, local_check, parseEventName(pPacket->opcode), pre_opcode);
            else if(pPacket->opcode == HCI_EVENT_REQ_SBC_FRAME)
                MODLOGD("[checkerr]rx:%02X,op:%04x,sum:%02X,%s(%02X)",
                        pPacket->seqn, pPacket->opcode, pPacket->check_sum, parseEventName(pPacket->opcode), pPacket->payload[0]);
            else
                MODLOGD("[checkerr]rx:%02X,op:%04x,sum:%02X(%02X),%s",
                        pPacket->seqn, pPacket->opcode, pPacket->check_sum, local_check, parseEventName(pPacket->opcode));

            if(((pPacket->opcode == HCI_EVENT_ACK) /*&& (pre_opcode == HCI_CMD_SBC_FRAME)*/)
                    || (pPacket->opcode == HCI_EVENT_REQ_SBC_FRAME))
                ; /// omit checkerr
            else
                return false;
        }

        /* seqn filter */
        if(!DataTrans_CheckSeqn(pPacket)) {
            uint8_t status = CMD_ACK_COMPLETE;

            if(pPacket->opcode == HCI_EVENT_REQ_SBC_FRAME)
                ;
            else
                hci_cmd_send_ack(pPacket, status);

#if 0
            char * buffer = snprintf_buffer(pPacket->payload, pPacket->len - 2);
            MODLOGD("[err]syncWord: %02X,rx_seqn: %02X,len: %d, opcode: %04x, %s, check_sum: %02X",
                    pPacket->sync_word, pPacket->seqn, pPacket->len, pPacket->opcode, buffer, pPacket->check_sum);
            MODLOGD("parser: event name: %s", parseEventName(pPacket->opcode));
            free(buffer);
#else
            if((pPacket->opcode == HCI_EVENT_ACK) || (pPacket->opcode == HCI_EVENT_REPORT_ERR_CODE))
                MODLOGD("[seqnerr]rx:%02X,op:%04x,sum:%02X,%s(%04X)",
                        pPacket->seqn, pPacket->opcode, pPacket->check_sum, parseEventName(pPacket->opcode), ((pPacket->payload[1] << 8) | pPacket->payload[0]));
            else if(pPacket->opcode == HCI_EVENT_REQ_SBC_FRAME)
                MODLOGD("[seqnerr]rx:%02X,op:%04x,sum:%02X,%s(%02X)",
                        pPacket->seqn, pPacket->opcode, pPacket->check_sum, parseEventName(pPacket->opcode), pPacket->payload[0]);
            else
                MODLOGD("[seqnerr]rx:%02X,op:%04x,sum:%02X,%s",
                        pPacket->seqn, pPacket->opcode, pPacket->check_sum, parseEventName(pPacket->opcode));
#endif

            if(pPacket->opcode == HCI_EVENT_REQ_SBC_FRAME)
                return true;
            else {
                memset(pPacket, 0, sizeof(HCI_PackTypeDef));
                return false;
            }
        } else {
            return true;
        }
    }

out:
    MODLOGD("[lenerr]:%d,%d, ri:%d, wi:%d, opcode: 0x%x", pPacket->len, len, LoopQueue.readIndex, LoopQueue.writeIndex, opcode);
#if 0
    MODLOGD("LoopQueue======================");
    for(i = 0; i < LOOP_QUEUE_MAX_SIZE; i++) {
        if(i % 16 == 0)
            fatal_printf("\n");

        fatal_printf("%02X", LoopQueue.buf[i]);
    }
    MODLOGD("================================");
    fatal_printf("\n");
#endif
    return false;
}

/**
  * @brief  Get a entire HCI commandpacket.
  * @param  pPacket: point to HCI packet struct.
  * @retval The decoded status of a entire packet.
  */
static bool DataTrans_GetHCIPacket(HCI_PackTypeDef * pPacket, uint16_t len)
{
    uint16_t pos = 0;
#if 0
    static uint16_t readIndex[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    static uint8_t idx = 0;
#endif
    /* Check data length which can read */
    if(len > HCI_PACK_MAX_SIZE) {
        len = HCI_PACK_MAX_SIZE;
    }

    while(len) {
        /* Find header */
        pos = DataTrans_FindData(HCI_SYNC_WORD, len);

        /* Check packet */
        if(pos == 1) {

            bool ret;
#if 0
            readIndex[idx] = LoopQueue.readIndex;
            idx++;
            if(idx == 8)
                idx = 0;
#endif
            ret = DataTrans_CollectHCIPacket(pPacket, len);

            //APP_PRINT_INFO3("[talkband][DataTrans_GetHCIPacket] ret = %d, pos = %d, len = %d!", ret, pos, DataTrans_GetFIFOLen());
            return ret;
        } else {
            /* Error handle: discard error data */
            uint8_t buf[HCI_PACK_SINGLE_DISACRD_SIZE];
            uint8_t discard_len = 0;

            MODLOGD("[poserr]:%d,%d, ri:%d, wi:%d", pos, len, LoopQueue.readIndex, LoopQueue.writeIndex);
#if 0
            {
                uint32_t i;
                extern bt_uart_cfg rtlbt_uart_port;

                bt_uart_dma_enable(rtlbt_uart_port.port_id, 0);
                bt_uart_enable(rtlbt_uart_port.port_id, 0);

                MODLOGD("last idx:%d", idx);
                for(i = 0; i < 8; i++)
                    MODLOGD("ri:%d", readIndex[i]);

                MODLOGD("================================");
                for(i = 0; i < LOOP_QUEUE_MAX_SIZE; i++) {
                    if(i % 16 == 0)
                        fatal_printf("\n");

                    fatal_printf("%02X", LoopQueue.buf[i]);
                }
                fatal_printf("\n");
                MODLOGD("================================");
                ASSERT(0);
            }
#endif
            if(pos > 1) {
                discard_len = ((pos - 1) > HCI_PACK_SINGLE_DISACRD_SIZE) ? HCI_PACK_SINGLE_DISACRD_SIZE : (pos - 1);
                DataTrans_ReadQueue(buf, discard_len);

                /* Update buffer length */
                if(len > discard_len) {
                    len -= discard_len;
                } else {
                    MODLOGD("break1");
                    break;
                }
            } else {
                /* No header in current queue */
                discard_len = (len > HCI_PACK_SINGLE_DISACRD_SIZE) ? HCI_PACK_SINGLE_DISACRD_SIZE : len;
                DataTrans_ReadQueue(buf, discard_len);
                MODLOGD("break2");
                break;
            }
        }
    }

    return false;
}

/**
  * @brief  Parse dataTrans HCI packet.
  * @param  pPacket: point to HCI packet struct.
  * @retval The decoded status of a entire packet.
  */
bool DataTrans_Parse(HCI_PackTypeDef * pPacket)
{
    uint16_t len = DataTrans_GetFIFOLen();

    if(len >= HCI_PACK_MIN_WIDTH) {
        bool ret;

        ret = DataTrans_GetHCIPacket(pPacket, len);

        if(ret == true) {
#if defined(DEBUG_UART_RX_HCI_PACKET)
#if 0
            char * buffer = snprintf_buffer(pPacket->payload, pPacket->len - 2);
            MODLOGD("syncWord: %02X,rx_seqn: %02X,len: %d, opcode: %04x, %s, check_sum: %02X",
                    pPacket->sync_word, pPacket->seqn, pPacket->len, pPacket->opcode, buffer, pPacket->check_sum);
            MODLOGD("parser: event name: %s", parseEventName(pPacket->opcode));
            free(buffer);
#else
            if((pPacket->opcode == HCI_EVENT_ACK) || (pPacket->opcode == HCI_EVENT_REPORT_ERR_CODE))
                MODLOGD("r:%02X,o:%04x,s:%02X,%s(%04X)",
                        pPacket->seqn, pPacket->opcode, pPacket->check_sum, parseEventName(pPacket->opcode), ((pPacket->payload[1] << 8) | pPacket->payload[0]));
            else if((pPacket->opcode == HCI_EVENT_REQ_SBC_FRAME) || (pPacket->opcode == HCI_EVENT_WATERWORLD_PLAY_STATUS))
                MODLOGD("r:%02X,o:%04x,s:%02X,%s(%02X)",
                        pPacket->seqn, pPacket->opcode, pPacket->check_sum, parseEventName(pPacket->opcode), pPacket->payload[0]);
            else
                MODLOGD("r:%02X,o:%04x,s:%02X,%s",
                        pPacket->seqn, pPacket->opcode, pPacket->check_sum, parseEventName(pPacket->opcode));
#endif
#endif
        } else {
            ///MODLOGD("parser: packet err %d", len);
        }

        return ret;
    }

    ///MODLOGD("len err %d", len);

    return false;
}

/**
  * @brief  Assemble HCI packet which to be sent.
  * @param  pSendBuf: buffer address whose data will need to send.
  * @param  opcode: command opcode.
  * @param  status: communication status.
  * @param  payLoadBuf: payload buffer address.
  * @retval Check status of a entire packet.
  */
void DataTrans_AssemblePacket(uint8_t * pSendBuf, uint16_t opcode,
                              uint8_t * payLoadBuf, uint16_t len)
{
    uint8_t * pBuf = pSendBuf + HCI_SYNC_WORD_WIDTH;
    uint16_t check_len = len + HCI_OPCODE_WIDTH + HCI_PACKET_LEN_WIDTH + HCI_SEQN_WIDTH;
#if defined(DEBUG_UART_TX_HCI_PACKET)
    uint8_t * payLoadBuf_bak = payLoadBuf;
    uint16_t len_bak = len;
    uint8_t tx_seqn = hci_trans_info.hci_tx_seqn;
    uint8_t     check_sum;
#endif

    *pSendBuf++ = HCI_SYNC_WORD;
    *pSendBuf++ = (hci_trans_info.hci_tx_seqn++) & 0xFF;
    *pSendBuf++ = (len + HCI_OPCODE_WIDTH) & 0xFF;
    *pSendBuf++ = ((len + HCI_OPCODE_WIDTH) >> 8) & 0xFF;
    *pSendBuf++ = opcode & 0xFF;
    *pSendBuf++ = (opcode >> 8) & 0xFF;
    while(len--) {
        *pSendBuf++ = *payLoadBuf++;
    }
    *pSendBuf = DataTrans_calc_checksum(pBuf, check_len);

#if defined(DEBUG_UART_TX_HCI_PACKET)
    {
        check_sum = *pSendBuf;
#if 0
        char * buffer = snprintf_buffer(payLoadBuf_bak, len_bak);
        MODLOGD("cmd opcode: %04X tx_seqn: %02X %s name: %s", opcode, tx_seqn, buffer, parseCmdName(opcode));
        free(buffer);
#else
        if(opcode == HCI_CMD_ACK)
            MODLOGD("t:%02X,o:%04X,s:%02X,%s(%04X)", tx_seqn, opcode, check_sum, parseCmdName(opcode), ((payLoadBuf_bak[1] << 8) | payLoadBuf_bak[0]));
        else if(opcode == HCI_CMD_REQ_SBC_OPERATION)
            MODLOGD("t:%02X,o:%04X,s:%02X,%s(%02d)", tx_seqn, opcode, check_sum, parseCmdName(opcode), payLoadBuf_bak[0]);
        else
            MODLOGD("t:%02X,o:%04X,s:%02X,%s", tx_seqn, opcode, check_sum, parseCmdName(opcode));
#endif
    }
#endif
}


void DataTrans_AssembleSBCPacket(uint8_t * pSendBuf, uint16_t opcode,
                                 uint8_t * payLoadBuf, uint16_t len)
{
    uint8_t * pBuf = pSendBuf + HCI_SYNC_WORD_WIDTH;
    uint16_t check_len = len + HCI_OPCODE_WIDTH + HCI_PACKET_LEN_WIDTH + HCI_SEQN_WIDTH;
    uint8_t * pCheckSumBuf = pSendBuf + HCI_SYNC_WORD_WIDTH + HCI_SEQN_WIDTH + HCI_PACKET_LEN_WIDTH + HCI_OPCODE_WIDTH + len;
#if defined(ASR_RTL8763BO_PORT) && defined(DEBUG_UART_TX_HCI_PACKET)
    uint8_t tx_seqn = hci_trans_info.hci_tx_seqn;
#endif

    *pSendBuf++ = HCI_SYNC_WORD;
    *pSendBuf++ = (hci_trans_info.hci_tx_seqn++) & 0xFF;
    *pSendBuf++ = (len + HCI_OPCODE_WIDTH) & 0xFF;
    *pSendBuf++ = ((len + HCI_OPCODE_WIDTH) >> 8) & 0xFF;
    *pSendBuf++ = opcode & 0xFF;
    *pSendBuf++ = (opcode >> 8) & 0xFF;

    // here, 3bytes frame head
    // here, sbc frame data

    *pCheckSumBuf = DataTrans_calc_checksum(pBuf, check_len);

#if defined(ASR_RTL8763BO_PORT) && defined(DEBUG_UART_TX_HCI_PACKET)
#if 0
    MODLOGD("cmd opcode: %04X tx_seqn: %02X name: %s checksum: %02X", opcode, tx_seqn, parseCmdName(opcode), *pCheckSumBuf);
#else
    MODLOGD("t:%02X,o:%04X,s:%02X,%s", tx_seqn, opcode, *pCheckSumBuf, parseCmdName(opcode));
#endif
#endif
}

void DataTrans_AssembleAudioPacket(uint8_t * pSendBuf, uint16_t opcode,
                                   uint8_t * payLoadBuf, uint16_t len)
{
    uint8_t * pBuf = pSendBuf + HCI_SYNC_WORD_WIDTH;
    uint16_t check_len = len + HCI_OPCODE_WIDTH + HCI_PACKET_LEN_WIDTH + HCI_SEQN_WIDTH;
    uint8_t * pCheckSumBuf = pSendBuf + HCI_SYNC_WORD_WIDTH + HCI_SEQN_WIDTH + HCI_PACKET_LEN_WIDTH + HCI_OPCODE_WIDTH + len;

    *pSendBuf++ = HCI_SYNC_WORD;
    *pSendBuf++ = (hci_trans_info.hci_tx_seqn++) & 0xFF;
    *pSendBuf++ = (len + HCI_OPCODE_WIDTH) & 0xFF;
    *pSendBuf++ = ((len + HCI_OPCODE_WIDTH) >> 8) & 0xFF;
    *pSendBuf++ = opcode & 0xFF;
    *pSendBuf++ = (opcode >> 8) & 0xFF;

    *pCheckSumBuf = DataTrans_calc_checksum(pBuf, check_len);
}

/**
 * @brief send packet by HCI layer.
 * @param pbuf: packet address.
 * @param len: packet length.
 * @return none
*/
void DataTrans_send(uint8_t * pbuf, uint16_t len)
{
    /* Check parameters */
    if((len < HCI_PACK_MIN_WIDTH) || (len > HCI_PACK_MAX_SIZE)) {
        APP_PRINT_ERROR1("[talkband] packet len = 0x%x!", len);
        return ;
    }
#if 0
    if((pbuf[4] == 0x00) && (pbuf[5] == 0x00)) {
        APP_PRINT_INFO1("[talkband][->hci send ack]: %b", TRACE_BINARY(len, pbuf));
    } else {
        APP_PRINT_INFO1("[talkband][->hci send cmd]: %b", TRACE_BINARY(len, pbuf));
    }
#endif
    DataTrans_uart_send(pbuf, len);
}

HCI_EVENT_CB bbpro_hci_event_cb __attribute__((weak)) = NULL;
/**
  * @brief  Fill in data to HCI packet.
  * @param  pPacket: point to HCI packet struct.
  * @retval Check status of a entire packet.
  */
bool DataTrans_HandleHCIPacket(HCI_PackTypeDef * pPacket)
{
    /* Parse packet */
    if(!DataTrans_Parse(pPacket)) {
        return false;
    }

    /* Notify to upper layer */
    if(bbpro_hci_event_cb != NULL) {
        bbpro_hci_event_cb(pPacket); // void talkband_bbpro_hci_event_cb(HCI_PackTypeDef *pPacket)
    }
    return true;
}

bool DataTrans_HandleRX(void)
{
    bool ret;

    ///uart_rx_lock();

    ret = DataTrans_HandleHCIPacket(&(hci_trans_info.hci_recv_pack));

    ///uart_rx_unlock();

    return ret;
}

#endif/*USE_LV_BLUETOOTH*/

/******************* (C) COPYRIGHT 2018 Realtek Semiconductor Corporation *****END OF FILE****/

